以 KMM 的 scope 來說,是不包含 UI 部分的,但這只是現階段沒有、不代表未來也沒有,我想以 JetBrains 這麼有企圖心的公司來說,未來是非常有可能朝這方面繼續推進的,而且使用 Kotlin 來建立 UI 這幾年有了新的進展,Google 在 Android 開發導入了 Jetpack Compose 這個新的 UI Framework,而 Jetbrains 又基於 Android 的 Jetpack Compose 延伸開發了 Compose Multiplatform,雖然現在還不支援 iOS,但怎麼說也是個非常符合我們 Kotlin 全面啟動的主題,今天就讓我們一起來看看吧~
由於我們的篇幅不夠完整地介紹整個 Compose 的使用,主要會著重在於探討設計面的心得與想法,許多與筆者同屆的參賽者都是以 Compose 為題目,大家有興趣也可以多多參考、或是直接到官網查詢相關資料。
Compose 的主要精髓在於 Declarative UI,中文的翻譯是宣告式 UI,那 Declarative 是什麼意思呢?
其實寫程式本質上是很容易有 bug 的,主要的原因就是寫程式太自由了,我們可以很容易就寫出很複雜的 code,這也是為什麼我們就算花了這麼多精神,學習了這麼多方法,還是無法寫出完美無瑕的程式碼,物件可以被傳來傳去到不同地方,而它的屬性更可能在不同的 thread 上被修改,在程式領域有一句話說:”這是 feature 不是 bug”,今天我們可以半開玩笑地用另一個角度來詮釋,往往就是因為程式語言有了這些 feature,才讓我們有了更多的 bug!
既然物件傳來傳去並修改值很容易出現問題,那可不可以我們主動限制程式的能力來避免問題呢,那就是當屬性一被宣告後,就不能改了,讓程式碼由原本非常自由發散的狀態,變成比較侷限容易理解的狀態,這種直接定義狀態後就不能修改的程式風格,其實就是宣告式,以下附上二種風格的程式碼讓大家對比一下:
// Imperative style
b.setColor(red)
b.clearChildren()
val c3 = ViewC(...)
b.add(c3)
// Declarative style
return ViewB(
color = red,
child = ViewC(...)
)
另外一個重點則是宣告式的寫法通常比較容易隱藏 implementation 的細節,因為我們沒有具體的叫程式該怎麼值型,就像我們去餐廳吃飯不會叫廚師該怎麼煮我們點的餐點一樣,比起自己煮飯,煮爛掉的機率就小多了。而這被隱藏的 implementation 細節也讓具體實作變得更好抽換以及更新,好處蠻多的。
此外這種宣告式的方式讓寫程式變得像寫設定檔一樣的單純好閱讀,而同樣的概念其實也不止 Compose 有使用,Flutter、SwiftUI 等其實也都有宣告式的精神在內。
這種透過更多限制來約束行為帶來更多好處的例子在程式界也是屢見不鮮,大家可以多想想還有哪些例子。
Compose 裡的 View 是由一個一個 Composable function 所組成,所謂的 Composable function 就是加了 @Composable
annotation 的 function,而跟 Coroutine 一樣只有 Composable function 可以呼叫 Composable function,範例如下:
@Composable
fun Greeting(names: List<String>) {
if (names.isEmpty()) {
Text("No Data yet")
} else {
for (name in names) {
Text("Hello $name")
}
}
}
Text
跟 Greeting
都是 Composable function,而我們也可以很清楚只看這個 function 就了解他想要完成的畫面,是不是很直覺呢?
另一個有趣的點是 Compose 是使用 function 不斷地串接,而不是用 class 繼承,這個特性很大程度地提供給我們更多的彈性去組合更多我們想要的功能,而不會被單一繼承鏈所限制住。
宣告式這種結構就剛剛的說明就是定義了以後就不能改,那聽起來好像不太適合描述一個動態的畫面, 尤其是會跟著 user 的互動而有變化的畫面更是如此,那 Compose 是怎麼描述一個動態的畫面呢?
主要是 state 的變化就會主動觸發所涵蓋的 Composable function 重新跑一次建立新的 tree,這個重新跑的過程就叫做 Recomposition,而 Compose 也做了蠻多優化讓它可以只觸發該重新跑的 Composable function 避免浪費,但這也代表著 Composable function 的執行順序、次數都不是我們可以控制的,也不該在這些 Composable function 裡直接去執行會有 side effect 的事情(比如修改 global 參數的值、打 api 等),這些都有可能造成不預期的結果。
Compose 還有很多很多東西可以探討,但核心來說大概就這些概念了,筆者是覺得 Compose 或是 Declaritive 的這種開發方式蠻有可能是未來的潮流的,雖然說 Compose 目前的 adoption rate 應該是不高,但就讓我們一起投入宣告式的懷抱吧!
最後宣傳個活動,大家如果對 Jetpack Compose 有興趣的話,10/19 新加坡的 Google Developer Relations Engineer - Sagar Begale 將會來台灣分享 跟著 Google Android DevRel 一起來學習 Compose 吧!,有興趣的朋友們不要錯過囉,活動連結:
https://gdg.community.dev/events/details/google-gdg-taipei-presents-gen-zhu-google-android-devrel-yi-qi-lai-xue-xi-compose-ba/